﻿/*
 * Copyright (C) 2023 - 2024 lchfj.cn
 * 
 * This file is part of FastestHost.
 * 
 * FastestHost is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * FastestHost is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with FastestHost. If not, see <https://www.gnu.org/licenses/>.
 */

using System.Diagnostics;
using System.IO.Compression;
using System.Security.Cryptography;
using Cn.Lchfj.FastestHost.Network;

namespace Cn.Lchfj.FastestHost
{
    internal class Update
    {
        internal static async Task<bool> GetHasCheckedTodayAsync()
        {
            var today = DateTime.Now.Date;
            if (today.DayOfWeek != DayOfWeek.Monday)
                return true;

            var path = Path.Combine(Path.GetTempPath(), "FastedHostUpdateCheck.tmp");

            var hasChecked = File.Exists(path) && File.GetLastWriteTime(path).Date == today;
            if (!hasChecked)
                await File.WriteAllTextAsync(path, today.Ticks.ToString());

            return hasChecked;
        }

        internal static async Task<UpdateInfo?> NeedUpdateAsync(Action<string> fail)
        {
            var info = await ApiClient.Instance.GetUpdateInfoAsync();
            if (info is null || !info.IsValidate())
            {
                fail("获取更新信息失败！");
                return null;
            }

            string currentHash;
            using (var stream = new FileStream(Program.CurrentPath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var hash = await SHA1.HashDataAsync(stream);
                currentHash = Convert.ToHexString(hash);
            }


            if (currentHash.Equals(info.Hash, StringComparison.OrdinalIgnoreCase))
            {
                ConsoleHelper.WriteLine("无需更新！");
                return null;
            }

            return info;
        }

        private static void ShowError(string reason)
        {
            if (string.IsNullOrWhiteSpace(ApiClient.AuthorWebSite))
            {
                ConsoleHelper.WriteLine(reason + "请从原始下载地址检查更新！");
                ConsoleHelper.ReadLine();
            }
            else
            {
                ConsoleHelper.WriteLine(reason + "输入【Y】并回车访问作者网站进行查看");
                var input = ConsoleHelper.ReadLine()?.Trim();
                if ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))
                {
                    RunHelper.RunProcessOutline("explorer", [ApiClient.AuthorWebSite]);
                }
            }
        }



        internal static async Task CheckUpdateAutoAsync()
        {
            var hasCheckedToday = await GetHasCheckedTodayAsync();
            if (hasCheckedToday)
                return;

            var updateInfo = await NeedUpdateAsync(s => { });
            if (updateInfo is null)
                return;

            ConsoleHelper.ShowWindow();

            ConsoleHelper.WriteLine("FastestHost 发现新版本，是否更新？");
            if (!string.IsNullOrWhiteSpace(updateInfo.Content))
            {
                ConsoleHelper.WriteLine("本次更新内容：");
                ConsoleHelper.WriteLine(updateInfo.Content);
                ConsoleHelper.WriteLine();
            }

            ConsoleHelper.WriteLine("输入【Y】并回车开始更新！");

            var input = ConsoleHelper.ReadLine()?.Trim();
            if ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))
                await DownloadFileAsync(updateInfo);

            ConsoleHelper.HiddenWindow();
        }
        internal static async Task CheckUpdateManualAsync()
        {
            var updateInfo = await NeedUpdateAsync(ShowError);
            if (updateInfo is null)
            {
                ConsoleHelper.ReadLine();
                return;
            }

            if (!string.IsNullOrWhiteSpace(updateInfo.Content))
            {
                ConsoleHelper.WriteLine("本次更新内容：");
                ConsoleHelper.WriteLine(updateInfo.Content);
                ConsoleHelper.WriteLine();
            }

            await DownloadFileAsync(updateInfo);
        }


        internal static async Task DownloadFileAsync(UpdateInfo info)
        {
            ConsoleHelper.WriteLine("正在下载更新文件···");

            var tempFile = Path.GetTempFileName();
            {
                using var httpClient = new HttpClient();
                using var response = await httpClient.GetAsync(info.Url, HttpCompletionOption.ResponseHeadersRead);
                if (!response.IsSuccessStatusCode || response.Content is null)
                {
                    ShowError("下载更新文件失败，CODE：" + response.StatusCode + "！");
                    return;
                }

                using (var stream = await response.Content.ReadAsStreamAsync())
                {
                    using var tempFileStream = File.OpenWrite(tempFile);
                    await stream.CopyToAsync(tempFileStream);
                }
            }

            var exePath = Path.ChangeExtension(tempFile, ".exe");
            using (var zip = new ZipArchive(File.OpenRead(tempFile)))
            {
                var file = zip.GetEntry("FastestHost.exe")!;
                file.ExtractToFile(exePath);
            }

            var argument = new RunSetting()
            {
                Command = RunSetting.CommandTypeEnums.UpdateFile,
                Path = [Program.CurrentPath],
                RunType = RunSetting.RunTypeEnums.Network,
            }.GetArgument();
            RunHelper.RunProcessOutline(exePath, [argument]);
        }

        internal static async Task UpdateFileAsync(RunSetting setting)
        {
            ConsoleHelper.ShowWindow();

            var targetPath = setting.Path[0];
            var processesName = Path.GetFileNameWithoutExtension(targetPath);

            ConsoleHelper.WriteLine("正在更新，请稍后！");
            ConsoleHelper.WriteLine("正在等待原始进程终止！");
            {
                var haveRun = true;
                while (haveRun)
                {
                    await Task.Delay(200);

                    haveRun = false;
                    var process = Process.GetProcessesByName(processesName);
                    foreach (var item in process)
                    {
                        if (item.Id == Program.CurrentProcessId)
                            continue;

                        if (!targetPath.Equals(item.MainModule!.FileName, StringComparison.OrdinalIgnoreCase))
                            continue;


                        haveRun = true;
                        item.Kill();
                    }
                }
            }


            ConsoleHelper.WriteLine("更新文件中！");
            File.Copy(Program.CurrentPath, targetPath, true);
            RunHelper.RunProcessOutline(targetPath);
        }
    }
}
